package com.hyt.it.ogt.kq.service.gov.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.base.BaseEntity;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.exception.NestedBusinessException;
import com.hyt.core.util.DateUtils;
import com.hyt.core.util.RemoteUtil;
import com.hyt.core.util.UUIDUtils;
import com.hyt.exception.exception.BusinessLogicException;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.enums.ExamAgreementEnum;
import com.hyt.it.ogt.kq.common.enums.UserBusinessAccessTypeEnum;
import com.hyt.it.ogt.kq.common.exception.GovInvalidRequestException;
import com.hyt.it.ogt.kq.common.gov.constant.*;
import com.hyt.it.ogt.kq.common.gov.utils.DateTimeUtil;
import com.hyt.it.ogt.kq.service.admission.IAdmissionTicketTemplateVariableService;
import com.hyt.it.ogt.kq.service.gov.api.ks.model.ExamTaskPageVO;
import com.hyt.it.ogt.kq.service.gov.api.ks.model.QueryExamTaskParam;
import com.hyt.it.ogt.kq.service.gov.api.ks.model.TaskBaseVO;
import com.hyt.it.ogt.kq.service.gov.api.platform.model.param.TaskInfoPageParam;
import com.hyt.it.ogt.kq.service.gov.api.platform.model.vo.TaskInfoPageVO;
import com.hyt.it.ogt.kq.service.gov.api.platform.model.vo.TaskStatisticsVO;
import com.hyt.it.ogt.kq.service.gov.feign.ks.KsClient;
import com.hyt.it.ogt.kq.service.gov.feign.ks.model.PushExamConfigVO;
import com.hyt.it.ogt.kq.service.gov.feign.pt.GovPtClient;
import com.hyt.it.ogt.kq.service.gov.mapper.ExaminerMapper;
import com.hyt.it.ogt.kq.service.gov.mapper.TaskMapper;
import com.hyt.it.ogt.kq.service.gov.mapper.TimeRoomExaminerMapper;
import com.hyt.it.ogt.kq.service.gov.model.dto.TaskDTO;
import com.hyt.it.ogt.kq.service.gov.model.entity.*;
import com.hyt.it.ogt.kq.service.gov.model.param.*;
import com.hyt.it.ogt.kq.service.gov.model.vo.ExamAgreementVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.OnlineTaskBaseVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskInfoVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TestClassifyVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.overview.ExamManageCollectVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.task.TaskPageVO;
import com.hyt.it.ogt.kq.service.gov.service.*;
import com.hyt.it.ogt.kq.service.model.entity.*;
import com.hyt.loginfo.model.User;
import com.hyt.loginfo.model.UserBusinessAccess;
import com.hyt.loginfo.model.param.UserBusinessAccessParam;
import com.hyt.loginfo.service.IBaseLoginService;
import com.hyt.loginfo.service.IUaBusinessAccessService;
import com.hyt.model.PageParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 考试任务表 服务实现类
 * </p>
 *
 * @author huangyh
 * @since 2021-03-08
 */
@Slf4j
@Service
public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implements ITaskService {

    @Autowired
    private IOfficeAreaService iOfficeAreaService;

    @Autowired
    private IOfficeCandidateAreaService iOfficeCandidateAreaService;

    @Autowired
    private IOfficeCandidateTicketTemplateService iOfficeCandidateTicketTemplateService;

    @Autowired
    private IOfficeFileTemplateService iOfficeFileTemplateService;

    @Autowired
    private IOfficePlaceService iOfficePlaceService;

    @Autowired
    private IOfficeRoomService iOfficeRoomService;

    @Autowired
    private IOfficeSoftService iOfficeSoftService;

    @Autowired
    private IOfficeTimeRoomParamService iOfficeTimeRoomParamService;

    @Autowired
    private IOfficeCodeService iOfficeCodeService;

    @Autowired
    private ITaskBehaviorConfigService iTaskBehaviorConfigService;

    @Autowired
    private IOfficeExamAgreementService iOfficeExamAgreementService;

    @Autowired
    private IExamAgreementService iExamAgreementService;

    @Autowired
    private IOfficeBreakRuleSchemeService iOfficeBreakRuleSchemeService;

    @Autowired
    private IBreakRuleSchemeService iBreakRuleSchemeService;

    @Resource
    private IOfficeBreakRuleBehaviorService iOfficeBreakRuleBehaviorService;

    @Resource
    private IBreakRuleBehaviorService iBreakRuleBehaviorService;

    @Resource
    private TaskMapper taskMapper;

    @Resource
    private ITaskConfigService iTaskConfigService;

    @Resource
    private IOfficeTaskConfigService iOfficeTaskConfigService;

    @Resource
    private IOfficeTaskKsConfigService iOfficeTaskKsConfigService;

    @Resource
    private ITaskKsConfigService iTaskKsConfigService;

    @Resource
    private ITimeRoomParamService iTimeRoomParamService;

    @Resource
    private ITaskCandidateAreaService iTaskCandidateAreaService;

    @Resource
    private ITaskAreaService iTaskAreaService;

    @Resource
    private ITaskPlaceService iTaskPlaceService;

    @Resource
    private ITaskRoomService iTaskRoomService;

    @Resource
    private ICandidateService iCandidateService;

    @Resource
    private ICandidatePhotoService iCandidatePhotoService;

    @Resource
    private ICandidateSubjectService iCandidateSubjectService;

    @Resource
    private ISubjectService iSubjectService;

    @Resource
    private ITimeService iTimeService;

    @Resource
    private ITimeSubjectService iTimeSubjectService;

    @Resource
    private ITimeRoomService iTimeRoomService;

    @Resource
    private ITimeRoomPasswordService iTimeRoomPasswordService;

    @Resource
    private IAgreementService iAgreementService;

    @Resource
    private IExamAreaService iExamAreaService;

    @Resource
    private IPaperService iPaperService;

    @Resource
    private ITimeSubjectPaperService iTimeSubjectPaperService;

    @Resource
    private ITimeRoomCandidateService iTimeRoomCandidateService;

    @Resource
    private IExamPackageService iExamPackageService;

    @Resource
    private ITaskTestInventoryService iTaskTestInventoryService;

    @Resource
    private IBaseLoginService iBaseLoginService;

    @Resource
    private KsClient ksClient;
    @Resource
    IUaBusinessAccessService iUaBusinessAccessService;
    @Resource
    private GovPtClient ptClient;
    @Resource
    IAdmissionTicketTemplateVariableService iAdmissionTicketTemplateVariableService;
    @Resource
    private ITimeRoomExaminerService iTimeRoomExaminerService;
    @Resource
    private ExaminerMapper examinerMapper;
    @Resource
    private TimeRoomExaminerMapper timeRoomExaminerMapper;

    @Override
    public Page<TaskPageVO> page(TaskPageParam pageParam, String officeId, Set<String> deptIds) {
        // 获取来自平台的任务ID
        List<String> businessIds = iUaBusinessAccessService.getBusinessPermissionById(iBaseLoginService.getUserId(), UserBusinessAccessTypeEnum.KW_TASK_PERMISSION.getCode());
        Page<TaskPageVO> taskPageVOPage = taskMapper.page(pageParam, officeId, businessIds, deptIds);
        taskPageVOPage.getRecords().stream().forEach(taskPageVO -> {
            if (TaskExamTypeEnum.ONLINE.getCode().equals(taskPageVO.getExamType())) {
                taskPageVO.setExamVersion("线上");
            } else {
                TaskConfig taskConfig = iTaskConfigService.lambdaQuery()
                        .eq(TaskConfig::getTaskId, taskPageVO.getId())
                        .eq(TaskConfig::getCode, "examVersion")
                        .last(LIMIT_1)
                        .one();

                taskPageVO.setExamVersion(taskConfig.getValue());
            }
        });
        return taskPageVOPage;
    }

    @Override
    public Page<TaskInfoPageVO> page(TaskInfoPageParam taskInfoPageParam) {
        //获取机构id
        String officeId = iBaseLoginService.getOfficeId();
        //组装考试任务主表查询语句
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(Task::getId, Task::getName)
                .eq(Task::getOfficeId, officeId)
                .and(wrapper -> wrapper.eq(Task::getBindPlatform, Boolean.FALSE).or().isNull(Task::getBindPlatform))
                .like(StringUtils.isNotBlank(taskInfoPageParam.getSearchText()), Task::getName, taskInfoPageParam.getSearchText())
                .eq(Task::getDelFlag, Boolean.FALSE);
        //获取分页参数
        PageParam<Task> pageParam = new PageParam<>();
        BeanUtils.copyProperties(taskInfoPageParam, pageParam);
        pageParam.changeOrders(Task.class);
        //获取考试任务主表分页数据
        Page<Task> taskPage = this.page(pageParam, queryWrapper);
        //转换考试任务信息数据
        List<TaskInfoPageVO> records = taskPage.getRecords()
                .stream().map(task -> {
                    //获取考试任务id
                    String taskId = task.getId();
                    //获取编排考生数
                    Integer arrangedCount = iTimeRoomCandidateService.countArrangedCandidate(taskId);
                    //获取科目数
                    Integer subjectCount = iSubjectService.countByTaskId(taskId);
                    //获取批次数
                    Integer timeCount = iTimeService.countByTaskId(taskId);
                    //设置考试任务信息数据
                    TaskInfoPageVO taskInfoPageVO = new TaskInfoPageVO();
                    taskInfoPageVO.setId(task.getId())
                            .setName(task.getName())
                            .setArrangedCount(arrangedCount)
                            .setSubjectCount(subjectCount)
                            .setTimeCount(timeCount);
                    return taskInfoPageVO;
                }).collect(Collectors.toList());
        Page<TaskInfoPageVO> page = new Page<>();
        BeanUtils.copyProperties(taskPage, page);
        page.setRecords(records);
        return page;
    }

    @Override
    public Page<ExamTaskPageVO> getExamTask(QueryExamTaskParam queryExamTaskParam) {

        String userType = queryExamTaskParam.getUserType();
        Page<ExamTaskPageVO> pageParam = queryExamTaskParam.getPageParam();
        Page<ExamTaskPageVO> page = null;
        // 获取来自平台的任务ID
        if (UserRoleType.USER_ROLE_TYPE_ADMIN.getCode().equals(userType)) {
            //管理员
            page = taskMapper.pageAdmin(pageParam, queryExamTaskParam.getOfficeId(), queryExamTaskParam.getDeptList(), null, queryExamTaskParam.getExamState(), queryExamTaskParam.getTaskName());
        } else if (UserRoleType.USER_ROLE_TYPE_PROCTOR.getCode().equals(userType)) {
            //获取监考员绑定的任务id
            List<String> taskIds = timeRoomExaminerMapper.selectTaskIdByProctor(iBaseLoginService.getUserId());
            //获取下级考务员绑定的任务id
            List<String> businessIds = iUaBusinessAccessService.getBusinessPermissionByTypes(iBaseLoginService.getUserId(), UserBusinessAccessTypeEnum.getKwTaskSelectForKs());
            //取两者的并集
            Set<String> resultSet = CollectionUtil.unionDistinct(businessIds,taskIds);
            List<String> resultList = resultSet.stream().collect(Collectors.toList());
            page = taskMapper.pageProctor(pageParam, queryExamTaskParam.getUserId(), queryExamTaskParam.getDeptList(), resultList, queryExamTaskParam.getExamState(), queryExamTaskParam.getTaskName());

        } else {
            throw new KqException(ResponseCode.ERROR_KS_USER_TYPE_NONE.getCode(), ResponseCode.ERROR_KS_USER_TYPE_NONE.getMsg());
        }

        List<ExamTaskPageVO> examTaskPageVOList = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(page.getRecords())){
            page.getRecords().stream().forEach(examTaskPageVO -> {
                //封装TaskBaseVO对象
                TaskBaseVO taskBaseVO = new TaskBaseVO();
                BeanUtils.copyProperties(examTaskPageVO, taskBaseVO);
                LocalDateTime now = DateTimeUtil.getFormatNow();

                if (now.isBefore(DateTimeUtil.getFormatDateTime(examTaskPageVO.getBeginDate()))) {
                    taskBaseVO.setState(String.valueOf(CodeEnum.EXAM_TIME_STATE_NONE.getCode()));
                } else if (now.isAfter(DateTimeUtil.getFormatDateTime(examTaskPageVO.getEndDate()))) {
                    taskBaseVO.setState(String.valueOf(CodeEnum.EXAM_TIME_STATE_OVER.getCode()));
                } else {
                    taskBaseVO.setState(String.valueOf(CodeEnum.EXAM_TIME_STATE_ING.getCode()));
                }

                examTaskPageVO.setExam(taskBaseVO);
                //任务批次信息
                List<Time> timeList = iTimeService.getByTaskId(taskBaseVO.getId());
                if (timeList != null && !timeList.isEmpty()) {
                    List<String> examIds = timeList.stream().map(Time::getId).collect(Collectors.toList());
                    examTaskPageVO.setExamIds(examIds);
                    taskBaseVO.setTimeCount(examIds.size());
                }

                //任务考场信息
                List<TaskRoom> taskRoomList = iTaskRoomService.listByTaskId(taskBaseVO.getId());
                if (taskRoomList != null && !taskRoomList.isEmpty()) {
                    List<String> examRoomIds = taskRoomList.stream().map(TaskRoom::getId).collect(Collectors.toList());
                    examTaskPageVO.setExamRoomIds(examRoomIds);
                    taskBaseVO.setRoomCount(examRoomIds.size());
                }

                examTaskPageVOList.add(examTaskPageVO);

            });
        }

        page.setRecords(examTaskPageVOList);
        return page;
    }

    @Override
    public Task getTaskByCode(String code) {
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(Task::getCode, code)
                .eq(Task::getDelFlag, false)
                .last("LIMIT 1");
        return getOne(queryWrapper);
    }

    @Override
    public Task getById(String id) {
        return this.lambdaQuery()
                .eq(Task::getId, id)
                .eq(Task::getDelFlag, false)
                .last("LIMIT 1")
                .one();
    }

    @Override
    public Boolean activeTaskByCode(String code) {
        Task task = this.getTaskByCode(code);
        if (ObjectUtils.isEmpty(task)) {
            //考试任务不存在
            return false;
        }
        if (task.getPublish()) {
            //考试任务已发布
            //考试任务结束时间在当前时间之后，考试任务在有效期内
            return task.getEndDate().isAfter(LocalDateTime.now());
        } else {
            //考试任务未发布
            return false;
        }
    }

    @Override
    public Boolean getPublishByCode(String taskCode) {
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(Task::getPublish)
                .eq(Task::getCode, taskCode)
                .eq(Task::getDelFlag, false);
        Task task = getOne(queryWrapper);
        if (ObjectUtils.isEmpty(task)) {
            //考试任务不存在
            return false;
        }
        //考试任务存在，返回发布状态
        return task.getPublish();
    }

    @Override
    public Task getNameByCode(String taskCode) {
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(Task::getName)
                .eq(Task::getCode, taskCode)
                .eq(Task::getDelFlag, false)
                .last("LIMIT 1");
        return getOne(queryWrapper);
    }

    @Override
    public Map<String, Object> query(String taskId) {

        Task task = this.lambdaQuery().eq(Task::getId, taskId).last("LIMIT 1").one();
        if (task == null) {
            throw new KqException(ResponseCode.TASK_NOT_EXIST.getCode(), "考试任务不存在。");
        }

        List<TaskConfig> taskConfigList = iTaskConfigService.lambdaQuery()
                .eq(TaskConfig::getTaskId, taskId)
                .eq(TaskConfig::getIsView, true)
                .eq(TaskConfig::getDelFlag, false)
                .list();

        Map<String, Object> map = new HashMap<>(16);
        map.put("taskInfo", task);

        if (taskConfigList == null || taskConfigList.isEmpty()) {
            return map;
        }

        Map<String, Object> mapExamConfig = new HashMap<>(16);
        Map<String, Object> mapExamClient = new HashMap<>(16);
        Map<String, Object> mapBehaviorConfig = new HashMap<>(16);
        taskConfigList.stream().forEach(taskConfig -> {

            String type = taskConfig.getType();
            //考试参数
            if (StringCodeEnum.TASK_CONFIG_TYPE_CONFIG.getCode().equals(type)) {
                mapExamConfig.put(taskConfig.getCode(), taskConfig.getValue());
            }

            //考试客户端参数
            if (StringCodeEnum.TASK_CONFIG_TYPE_CLIENT.getCode().equals(type)) {
                mapExamClient.put(taskConfig.getCode(), taskConfig.getValue());
            }

            //行为分析参数
            if (StringCodeEnum.TASK_CONFIG_TYPE_BEHAVIOR.getCode().equals(type)) {
                mapBehaviorConfig.put(taskConfig.getCode(), taskConfig.getValue());
            }

        });

        map.put("examConfig", mapExamConfig);
        map.put("examClient", mapExamClient);
        map.put("examBehavior", mapBehaviorConfig);

        return map;
    }

    @Override
    public Map<String, Object> defaultTaskConfig(String officeId) {

        List<OfficeTaskConfig> officeTaskConfigList = iOfficeTaskConfigService.queryOfficeTaskConfigView(officeId);
        Map<String, Object> map = new HashMap<>(16);
        if (officeTaskConfigList == null || officeTaskConfigList.isEmpty()) {
            return map;
        }

        Map<String, Object> mapExamConfig = new HashMap<>(16);
        Map<String, Object> mapExamClient = new HashMap<>(16);
        Map<String, Object> mapBehaviorConfig = new HashMap<>(16);

        officeTaskConfigList.stream().forEach(officeTaskConfig -> {
            String type = officeTaskConfig.getType();
            //考试参数
            if (StringCodeEnum.TASK_CONFIG_TYPE_CONFIG.getCode().equals(type)) {
                mapExamConfig.put(officeTaskConfig.getCode(), officeTaskConfig.getValue());
            }

            //考试客户端参数
            if (StringCodeEnum.TASK_CONFIG_TYPE_CLIENT.getCode().equals(type)) {
                mapExamClient.put(officeTaskConfig.getCode(), officeTaskConfig.getValue());
            }

            //行为分析参数
            if (StringCodeEnum.TASK_CONFIG_TYPE_BEHAVIOR.getCode().equals(type)) {
                mapBehaviorConfig.put(officeTaskConfig.getCode(), officeTaskConfig.getValue());
            }

        });

        map.put("examConfig", mapExamConfig);
        map.put("examClient", mapExamClient);
        map.put("examBehavior", mapBehaviorConfig);

        return map;
    }

    @Override
    public TaskParam query(Task task) {
        TaskParam taskParam = new TaskParam();
        String id = task.getId();
        //获取考试任务配置数据
        List<TaskConfig> taskConfigList = iTaskConfigService.getByTaskId(id);
        //获取行为分析配置数据
        QueryWrapper<TaskBehaviorConfig> taskBehaviorConfigQueryWrapper = new QueryWrapper<>();
        taskBehaviorConfigQueryWrapper.lambda().eq(TaskBehaviorConfig::getTaskId, id);
        TaskBehaviorConfig taskBehaviorConfig = iTaskBehaviorConfigService.getOne(taskBehaviorConfigQueryWrapper);
        //组装数据
        taskParam.setTask(task)
                .setTaskConfig(taskConfigList)
                .setTaskBehaviorConfig(taskBehaviorConfig);
        return taskParam;
    }

    @Override
    public Task getTaskById(String id) {
        QueryWrapper<Task> taskQueryWrapper = new QueryWrapper<>();
        taskQueryWrapper.lambda()
                .eq(Task::getId, id)
                .eq(Task::getDelFlag, false);
        return getOne(taskQueryWrapper);
    }

    @Override
    public TaskBaseVO getTaskInfoById(String id) {
        Task task = getTaskById(id);
        TaskBaseVO taskBaseVO = new TaskBaseVO();
        taskBaseVO.setId(task.getId())
                .setExamName(task.getName())
                .setBeginDate(DateTimeUtil.getDateTimeString(task.getBeginDate()))
                .setEndDate(DateTimeUtil.getDateTimeString(task.getEndDate()))
                .setTestClassify(task.getTestClassify());

        return taskBaseVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(String json) {
        Map<String, Object> map = (Map<String, Object>) JSON.parseObject(json, Map.class);
        Map<String, String> taskInfo = (Map<String, String>) map.get("taskInfo");
        String taskId = taskInfo.get("id").toString();
        //校验任务数据锁定
        this.isLockDataThrowException(taskId);
        //更新任务表
        updateTask(taskInfo);
        //更新任务参数表
        updateTaskConfig(taskId, map);
    }

    private void updateTask(Map<String, String> taskInfo) {
        String taskId = taskInfo.get("id");
        String name = taskInfo.get("name");
        LocalDateTime beginDate = DateTimeUtil.getFormatDateTime(taskInfo.get("beginDate"));
        LocalDateTime endDate = DateTimeUtil.getFormatDateTime(taskInfo.get("endDate"));

        Task task = this.lambdaQuery()
                .eq(Task::getId, taskId)
                .last("LIMIT 1")
                .one();

        if (!task.getName().equals(name) || !beginDate.isEqual(task.getBeginDate()) || endDate.isEqual(task.getEndDate())) {
            this.lambdaUpdate()
                    .eq(Task::getId, taskId)
                    .set(Task::getName, name)
                    .set(Task::getBeginDate, beginDate)
                    .set(Task::getEndDate, endDate)
                    .update();
        }
    }

    private void updateTaskConfig(String taskId, Map<String, Object> map) {
        List<TaskConfig> taskConfigList = new ArrayList<>();
        Map<String, String> examConfig = (Map<String, String>) map.get("examConfig");
        examConfig.forEach((k, v) -> {
            TaskConfig taskConfig = new TaskConfig();
            taskConfig.setTaskId(taskId)
                    .setCode(k)
                    .setValue(v);
            taskConfigList.add(taskConfig);
        });

        Map<String, String> examBehavior = (Map<String, String>) map.get("examBehavior");
        examBehavior.forEach((k, v) -> {
            TaskConfig taskConfig = new TaskConfig();
            taskConfig.setTaskId(taskId)
                    .setCode(k)
                    .setValue(v);
            taskConfigList.add(taskConfig);
        });

        Map<String, String> examClient = (Map<String, String>) map.get("examClient");
        examClient.forEach((k, v) -> {
            TaskConfig taskConfig = new TaskConfig();
            taskConfig.setTaskId(taskId)
                    .setCode(k)
                    .setValue(v);
            taskConfigList.add(taskConfig);
        });

        if (taskConfigList != null && !taskConfigList.isEmpty()) {
            iTaskConfigService.updateByCode(taskConfigList);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(TaskParam taskParam) {
        //参数校验
        //更新考试任务数据
        this.updateTask(taskParam);
        //更新考试任务配置数据
        this.updateTaskConfig(taskParam);
        //更新考试系统配置数据
        this.updateTaskExamSysConfig(taskParam);
        //更新行为分析配置数据
        this.updateTaskBehaviorConfig(taskParam);
    }

    /**
     * 更新行为分析配置
     *
     * @param taskParam
     * @throws NestedBusinessException
     */
    protected void updateTaskBehaviorConfig(TaskParam taskParam) throws NestedBusinessException {
        if (!iTaskBehaviorConfigService.updateById(taskParam.getTaskBehaviorConfig())) {
            throw new NestedBusinessException("更新行为分析配置表失败");
        }
    }

    /**
     * 更新考试系统配置
     *
     * @param taskParam 考试任务参数
     * @throws NestedBusinessException 异常
     */
    protected void updateTaskExamSysConfig(TaskParam taskParam) throws NestedBusinessException {
//        if (!iTaskExamSysConfigService.updateById(taskParam.getTaskExamSysConfig())) {
//            throw new NestedBusinessException("更新考试系统配置表失败");
//        }
    }

    /**
     * 更新考试任务配置
     *
     * @param taskParam 考试任务参数
     * @throws NestedBusinessException 异常
     */
    protected void updateTaskConfig(TaskParam taskParam) throws NestedBusinessException {
//        if (!iTaskConfigService.updateById(taskParam.getTaskConfig())) {
//            throw new NestedBusinessException("更新考试任务配置表失败");
//        }
    }

    /**
     * 更新考试任务
     *
     * @param taskParam 考试任务参数
     * @throws NestedBusinessException 异常
     */
    protected void updateTask(TaskParam taskParam) throws NestedBusinessException {
        TaskDTO taskDTO = new TaskDTO();
        BeanUtils.copyProperties(taskParam.getTask(), taskDTO);
        if (!taskMapper.updateTask(taskDTO)) {
            throw new NestedBusinessException("更新考试任务表失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String add(String json, String officeId) {
        Map<String, Object> map = (Map<String, Object>) JSON.parseObject(json, Map.class);
        String taskId = UUIDUtils.newSortUUID();

        //新增任务表
        Map<String, String> taskInfo = (Map<String, String>) map.get("taskInfo");

        Task task = new Task();
        task.setPublish(false)
                .setOfficeId(officeId)
                .setBeginDate(DateTimeUtil.getFormatDateTime(taskInfo.get("beginDate")))
                .setEndDate(DateTimeUtil.getFormatDateTime(taskInfo.get("endDate")))
                .setName(taskInfo.get("name"))
                .setType(CodeEnum.TASK_TYPE_FORMAL.getCode())
                .setExamType(taskInfo.get("examType"))
                .setDataLock(false)
                .setDelFlag(false)
                .setId(taskId);

        // 同时操作的情况会存在重复。
        task.setCode(getNewTaskCode(task.getBeginDate()));

        this.save(task);

        //初始化任务参数表
        iTaskConfigService.initTaskConfig(taskId, officeId);

        //更新任务参数表
        updateTaskConfig(taskId, map);

        return taskId;
    }

    /**
     * 线上机考初始化数据（初始化考生区域数据、考点区域数据）
     *
     * @param taskId 考试任务id
     */
    @Override
    public void initByOnline(String taskId, String officeId) throws Exception {
        //初始化任务参数
        iTaskKsConfigService.initTaskKsConfig(taskId, officeId);
        //初始化考生区域数据
        //iTaskCandidateAreaService.initVirtualTaskCandidateArea(taskId);
        //初始化考点区域数据
        //iTaskAreaService.initVirtualAreaPlace(taskId);
        //4.初始化考生区域数据
        iTaskCandidateAreaService.initTaskCandidateArea(taskId, officeId);
        //5.初始化考点考场区域数据
        iTaskAreaService.initTaskArea(taskId, officeId);
        //考生须知和欢迎语
        iExamAgreementService.initTaskExamAgreement(taskId, officeId);
        //初始化考生编排数据
        iTimeRoomParamService.initTaskTimeRoomParamFromOffice(taskId, officeId);
        //初始化监考员编排参数数据
        examinerMapper.initProctorParam(taskId);
    }

    @Override
    public void initTaskData(String taskId, String officeId) throws Exception {
        Task task = getById(taskId);
        if (TaskExamTypeEnum.ONLINE.getCode().equals(task.getExamType())) {
            initByOnline(taskId, officeId);
        } else {
            initTaskDataOffline(taskId, officeId);
        }

    }

    @Override
    public void initTaskDataOffline(String taskId, String officeId) throws Exception {
        Long start = System.currentTimeMillis();
        //1.初始化考生协议， 线下机考是上传文件包，暂时还没想到好的方案
        //2.初始化考生违纪处理办法数据
        iBreakRuleSchemeService.initBreakRuleSchemeFromOffice(taskId, officeId);
        //3.初始化考生违纪行为数据
        iBreakRuleBehaviorService.initBreakRuleBehaviorFromOffice(taskId, officeId);
        //4.初始化考生区域数据
        iTaskCandidateAreaService.initTaskCandidateArea(taskId, officeId);
        //5.初始化考点考场区域数据
        iTaskAreaService.initTaskArea(taskId, officeId);
        //6.初始化考生编排数据
        iTimeRoomParamService.initTaskTimeRoomParamFromOffice(taskId, officeId);
        // 特殊处理，线下模式没有虚拟考场编排
        iTimeRoomParamService.lambdaUpdate().set(TimeRoomParam::getValue, 1).eq(TimeRoomParam::getTaskId, taskId)
                .eq(TimeRoomParam::getCode, StringCodeEnum.ARRANGE_PARAM_ARRANGE_TYPE.getCode())
                .eq(BaseEntity::getDelFlag, false).update();
        //8.初始化任务参数表
        iTaskConfigService.initTaskConfig(taskId, officeId);
        //9.初始化测试项清单
        iTaskTestInventoryService.init(taskId, officeId);
        //初始化监考员编排参数数据
        examinerMapper.initProctorParam(taskId);
        Long end = System.currentTimeMillis();
        log.info("初始化线下机考参数耗时: {}", end - start);
    }

    @Override
    @Transactional(rollbackFor = NestedBusinessException.class)
    public void add(TaskParam taskParam, String officeId) throws GovInvalidRequestException {
        //参数校验
        this.checkTaskParam(taskParam, officeId);
        //生成考试任务编号
        this.generateTaskCode(taskParam, officeId);
        //插入考试任务数据
        this.insertTask(taskParam);
        //插入考试任务配置数据
        this.insertTaskConfig(taskParam);
        //插入考试系统配置数据
        this.insertTaskExamSysConfig(taskParam);
        //插入行为分析配置数据
        this.insertTaskBehaviorConfig(taskParam);
        //获取考试任务id
        String taskId = taskParam.getTask().getId();
        //插入默认考试协议数据
        this.insertExamAgreement(taskId, officeId);
        //插入默认违纪处理数据
        this.insertBreakRuleScheme(taskId, officeId);
        //插入默认违纪行为数据
        this.insertBreakRuleBehavior(taskId, officeId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTaskPublishStatus(TaskPublishParam taskPublishParam) throws KqException {
        if (CodeEnum.UNPUBLISHED.getCode().equals(taskPublishParam.getStatus())) {
            //取消发布任务
            this.lambdaUpdate()
                    .eq(Task::getId, taskPublishParam.getTaskId())
                    .set(Task::getPublish, false)
                    .update();
        } else if (CodeEnum.PUBLISHED.getCode().equals(taskPublishParam.getStatus())) {
            //发布任务
            if (!iTimeRoomCandidateService.timeRoomCandidateExits(taskPublishParam.getTaskId())) {
                //任务下还没有编排考生，不允许发布
                throw new KqException(ResponseCode.TASK_NOT_FIND_CANDIDATE_PUBLISH_FORBID.getCode(),
                        "任务下还没有编排考生，不允许发布");
            }
            Task task = this.lambdaQuery().select(Task::getId, Task::getDataLock).eq(Task::getId, taskPublishParam.getTaskId()).one();
            if (ObjectUtil.isNotEmpty(task) && !task.getDataLock()) {
                throw new KqException(ResponseCode.TASK_NOT_LOCK_PUBLISH_FORBID.getCode(),
                        "考生编排未锁定，不允许发布");
            }
            this.lambdaUpdate()
                    .eq(Task::getId, taskPublishParam.getTaskId())
                    .set(Task::getPublish, true)
                    .update();
        } else {
            //任务发布状态不存在
            throw new KqException(ResponseCode.TASK_PUBLISH_STATUS_NOT_FOUND.getCode(),
                    "任务发布状态不存在");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyTask(CopyTaskParam copyTaskParam) throws KqException {
        //获取被复制的任务
        Task task = this.getById(copyTaskParam.getTaskId());
        //校验被复制的任务是否存在
        if (ObjectUtils.isEmpty(task)) {
            throw new KqException(ResponseCode.TASK_NOT_EXIST.getCode(),
                    "考试任务不存在");
        }
        //校验是否线下机考，若为线上机考则不允许复制
        TaskExamTypeEnum taskExamTypeEnum = TaskExamTypeEnum.getByCode(task.getExamType());
        if (TaskExamTypeEnum.ONLINE.equals(taskExamTypeEnum)) {
            throw new KqException(ResponseCode.NOT_ALLOWED_TO_COPY_ONLINE_EXAM.getCode(),
                    "不允许复制线上机考模式的考试任务");
        }
        //获取用户名
        String userName = iBaseLoginService.getUserId();

        //旧id-新id映射关系
        Map<String, String> oldNewIdMap = new HashMap<>(16);
        //1.复制任务主表
        String oldTaskId = this.copyTaskMain(task, oldNewIdMap, copyTaskParam);
        //新的任务id
        String newTaskId = task.getId();
        //3.复制任务区域表
        this.copyTaskArea(newTaskId, oldTaskId, userName);
        //4.复制任务考点考场数据
        this.copyTaskRoomPlace(oldTaskId, oldNewIdMap);
        //5.复制科目信息表
        this.copySubject(oldTaskId, oldNewIdMap);
        //6.复制批次信息表
        this.copyTime(oldTaskId, oldNewIdMap);
        //7.复制批次科目表
        this.copyTimeSubject(oldTaskId, oldNewIdMap);
        //8.复制场次信息表
        this.copyTimeRoom(oldTaskId, oldNewIdMap);
        //9.复制编排参数表
        this.copyTimeRoomParam(oldTaskId, newTaskId, userName);
        //18.复制考生表
        this.copyCandidate(oldTaskId, oldNewIdMap);
        //19.复制考生照片表
        this.copyCandidatePhoto(oldTaskId, oldNewIdMap);
        //20.复制考生科目表
        this.copyCandidateSubject(oldTaskId, oldNewIdMap);
        //21.复制考生区域表
        this.copyCandidateArea(oldTaskId, oldNewIdMap);
        //21.复制编排信息表
        this.copyTimeRoomCandidate(oldTaskId, oldNewIdMap);

        if (TaskExamTypeEnum.ONLINE.getCode().equals(task.getExamType())) {
            //线上模式
            //复制任务配置
            iTaskKsConfigService.copyTaskKsConfig(newTaskId, oldTaskId, userName);
            //复制欢迎语
            iExamAgreementService.copyTaskExamAgreement(newTaskId, oldTaskId, userName);
        } else {
            //线下模式
            //2.复制任务配置表
            this.copyTaskConfig(newTaskId, oldTaskId, userName);
            //10.复制场次密码表
            this.copyTimeRoomPassword(oldTaskId, oldNewIdMap);
            //11.复制违纪处理表
            this.copyBreakRuleScheme(oldTaskId, oldNewIdMap);
            //12.复制违纪行为表
            this.copyBreakRuleBehavior(oldTaskId, oldNewIdMap);
            //13.复制考试协议表
            this.copyAgreement(oldTaskId, newTaskId, userName);
            //14.复制基础包表
            this.copyBaseDataPackage(newTaskId, oldTaskId);
            //15.复制考区表
            this.copyExamArea(oldTaskId, newTaskId, userName);
            //16.复制试卷表
            this.copyPaper(oldTaskId, oldNewIdMap);
            //17.复制批次科目试卷表
            this.copyTimeSubjectPaper(oldTaskId, oldNewIdMap);
        }

        //记录结束日志
        log.info("结束复制任务，旧id: {}，新id: {}，结束时间: {}",
                oldTaskId, newTaskId, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }

    /**
     * 复制任务主表
     *
     * @param task          旧的任务主表数据
     * @param oldNewIdMap   旧id-新id映射关系
     * @param copyTaskParam 复制任务参数
     * @return 旧的任务id
     * @throws KqException 异常
     */
    private String copyTaskMain(Task task, Map<String, String> oldNewIdMap, CopyTaskParam copyTaskParam) throws KqException {
        //旧的任务id
        String oldId = task.getId();
        //新的任务id
        String newId = UUIDUtils.newSortUUID();
        //记录开始日志
        log.info("开始复制任务，旧id: {}，新id: {}，开始时间: {}",
                oldId, newId, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        //生成新的任务编号
        task.setCode(this.getNewTaskCode(task.getBeginDate()))
                //设置任务类型
                .setType(copyTaskParam.getType())
                //设置任务名称
                .setName(copyTaskParam.getName())
                //设置报名id(复制任务报名id统一设置为"0")
                .setTaskBmId("0")
                //生成新的任务id
                .setId(newId);
        //插入任务主表
        if (!save(task)) {
            //插入任务主表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入任务主表失败");
        }
        //放入旧任务id-新任务id映射关系
        oldNewIdMap.put(oldId, newId);
        return oldId;
    }

    /**
     * 复制任务配置表
     *
     * @param oldTaskId 旧任务id
     * @param newTaskId 新任务id
     * @throws KqException 异常
     */
    private void copyTaskConfig(String newTaskId, String oldTaskId, String userName) throws KqException {
        iTaskConfigService.copyTaskConfig(newTaskId, oldTaskId, userName);
    }

    /**
     * 复制任务区域表
     *
     * @param newTaskId 新任务id
     * @param oldTaskId 旧任务id
     * @param userName  用户名
     * @throws KqException 异常
     */
    private void copyTaskArea(String newTaskId, String oldTaskId, String userName) throws KqException {
        iTaskAreaService.copyTaskArea(newTaskId, oldTaskId, userName);
    }

    /**
     * 复制任务考点考场数据
     *
     * @param oldTaskId   旧任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyTaskRoomPlace(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取旧的任务考点数据
        List<TaskPlace> taskPlaceList = iTaskPlaceService.listByTaskId(oldTaskId);
        //获取旧的任务考场数据
        List<TaskRoom> taskRoomList = iTaskRoomService.listByTaskId(oldTaskId);
        //替换考点、考场id
        taskPlaceList.forEach(place -> {
            String oldPlaceId = place.getId();
            String newPlaceId = UUIDUtils.newSortUUID();
            taskRoomList.forEach(room -> {
                if (place.getId().equals(room.getPlaceId())) {
                    String oldRoomId = room.getId();
                    String newRoomId = UUIDUtils.newSortUUID();
                    //考场设置新的考点id
                    room.setPlaceId(newPlaceId)
                            //设置新的任务id
                            .setTaskId(oldNewIdMap.get(oldTaskId))
                            //设置新的主键id
                            .setId(newRoomId);
                    //设置旧考场id-新考场id映射关系
                    oldNewIdMap.put(oldRoomId, newRoomId);
                }
            });
            //考点设置新的任务id
            place.setTaskId(oldNewIdMap.get(oldTaskId))
                    //设置新的主键id
                    .setId(newPlaceId);
            //设置旧考点id-新考点id映射关系
            oldNewIdMap.put(oldPlaceId, newPlaceId);
        });
        //插入任务考点表
        if (!iTaskPlaceService.saveBatch(taskPlaceList)) {
            //插入任务考点表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入任务考点表失败");
        }
        //插入任务考场表
        if (!iTaskRoomService.saveBatch(taskRoomList)) {
            //插入任务考场表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入任务考场表失败");
        }
    }

    /**
     * 复制科目信息表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copySubject(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的科目数据
        List<Subject> subjectList = iSubjectService.getSubjectByTaskId(oldTaskId);
        //替换id
        subjectList.forEach(subject -> {
            //旧科目id
            String oldSubjectId = subject.getId();
            //新科目id
            String newSubjectId = UUIDUtils.newSortUUID();
            //替换任务id
            subject.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换主键id
                    .setId(newSubjectId);
            //替换科目对应的父节点id
            subjectList.forEach(s -> {
                if (oldSubjectId.equals(s.getParentId())) {
                    s.setParentId(newSubjectId);
                }
            });
            //设置旧科目id-新科目id的映射关系
            oldNewIdMap.put(oldSubjectId, newSubjectId);
        });
        //插入科目信息表
        if (!iSubjectService.saveBatch(subjectList)) {
            //插入科目信息表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入科目信息表失败");
        }
    }

    /**
     * 复制批次信息表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyTime(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的批次数据
        List<Time> timeList = iTimeService.getByTaskId(oldTaskId);
        //替换id
        timeList.forEach(time -> {
            String oldTimeId = time.getId();
            String newTimeId = UUIDUtils.newSortUUID();
            //替换任务id
            time.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换主键id
                    .setId(newTimeId);
            oldNewIdMap.put(oldTimeId, newTimeId);
        });
        //插入批次信息表
        if (!iTimeService.saveBatch(timeList)) {
            //插入批次信息失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入批次信息失败");
        }
    }

    /**
     * 复制批次科目表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyTimeSubject(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取被复制的批次科目数据
        List<TimeSubject> timeSubjectList = iTimeSubjectService.listByTaskId(oldTaskId);
        //替换id
        timeSubjectList.forEach(timeSubject -> {
            String oldTimeSubjectId = timeSubject.getId();
            String newTimeSubjectId = UUIDUtils.newSortUUID();
            //替换任务id
            timeSubject.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换批次id
                    .setTimeId(oldNewIdMap.get(timeSubject.getTimeId()))
                    //替换科目id
                    .setSubjectId(oldNewIdMap.get(timeSubject.getSubjectId()))
                    //替换主键id
                    .setId(newTimeSubjectId);
            //设置旧批次科目id-新批次科目id映射关系
            oldNewIdMap.put(oldTimeSubjectId, newTimeSubjectId);
        });
        //插入批次科目表
        if (!iTimeSubjectService.saveBatch(timeSubjectList)) {
            //插入批次科目表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入批次科目表失败");
        }
    }

    /**
     * 复制场次信息表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyTimeRoom(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的场次数据
        List<TimeRoom> timeRoomList = iTimeRoomService.getByTaskId(oldTaskId);
        //替换id
        timeRoomList.forEach(timeRoom -> {
            String oldTimeRoomId = timeRoom.getId();
            String newTimeRoomId = UUIDUtils.newSortUUID();
            //替换任务id
            timeRoom.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换批次id
                    .setTimeId(oldNewIdMap.get(timeRoom.getTimeId()))
                    //替换考场id
                    .setRoomId(oldNewIdMap.get(timeRoom.getRoomId()))
                    //替换主键id
                    .setId(newTimeRoomId);
            //设置旧场次id-新场次id对应关系
            oldNewIdMap.put(oldTimeRoomId, newTimeRoomId);
        });
        //插入场次信息表
        if (!iTimeRoomService.saveBatch(timeRoomList)) {
            //插入场次信息表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入场次信息表失败");
        }
    }

    /**
     * 复制编排参数
     *
     * @param oldTaskId 被复制的任务id
     * @param newTaskId 新的任务id
     * @param userName  用户名
     */
    private void copyTimeRoomParam(String oldTaskId, String newTaskId, String userName) {
        iTimeRoomParamService.copyTimeRoomParam(oldTaskId, newTaskId, userName);
    }

    /**
     * 复制场次密码表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyTimeRoomPassword(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的场次密码数据
        List<TimeRoomPassword> timeRoomPasswordList = iTimeRoomPasswordService.getByTaskId(oldTaskId);
        //替换id
        timeRoomPasswordList.forEach(timeRoomPassword -> {
            //替换任务id
            timeRoomPassword.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换场次id
                    .setTimeRoomId(oldNewIdMap.get(timeRoomPassword.getTimeRoomId()))
                    //替换考场id
                    .setRoomId(oldNewIdMap.get(timeRoomPassword.getRoomId()))
                    //替换批次id
                    .setTimeId(oldNewIdMap.get(timeRoomPassword.getTimeId()))
                    //替换主键id
                    .setId(UUIDUtils.newSortUUID());
        });
        //插入场次密码表
        if (!iTimeRoomPasswordService.saveBatch(timeRoomPasswordList)) {
            //插入场次密码表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入场次密码表失败");
        }
    }

    /**
     * 复制违纪处理表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyBreakRuleScheme(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的违纪处理数据
        List<BreakRuleScheme> breakRuleSchemeList = iBreakRuleSchemeService.listByTaskId(oldTaskId);
        //替换id
        breakRuleSchemeList.forEach(breakRuleScheme -> {
            String oldBreakRuleSchemeId = breakRuleScheme.getId();
            String newBreakRuleSchemeId = UUIDUtils.newSortUUID();
            //替换任务id
            breakRuleScheme.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换主键id
                    .setId(newBreakRuleSchemeId);
            //设置旧违纪处理id-新违纪处理id的映射关系
            oldNewIdMap.put(oldBreakRuleSchemeId, newBreakRuleSchemeId);
        });
        //插入违纪处理表
        if (!iBreakRuleSchemeService.saveBatch(breakRuleSchemeList)) {
            //插入违纪处理表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入违纪处理表失败");
        }
    }

    /**
     * 复制违纪行为表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyBreakRuleBehavior(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的违纪行为数据
        List<BreakRuleBehavior> breakRuleBehaviorList = iBreakRuleBehaviorService.listByTaskId(oldTaskId);
        //替换id
        breakRuleBehaviorList.forEach(breakRuleBehavior -> {
            //替换任务id
            breakRuleBehavior.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换违纪处理id
                    .setSchemeId(oldNewIdMap.get(breakRuleBehavior.getSchemeId()))
                    //替换主键id
                    .setId(UUIDUtils.newSortUUID());
        });
        //插入违纪行为表
        if (!iBreakRuleBehaviorService.saveBatch(breakRuleBehaviorList)) {
            //插入违纪行为表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入违纪行为表失败");
        }
    }

    /**
     * 复制考试协议
     *
     * @param oldTaskId 被复制的任务id
     * @param newTaskId 新的任务id
     * @param userName  用户名
     */
    private void copyAgreement(String oldTaskId, String newTaskId, String userName) {
        iAgreementService.copyAgreement(oldTaskId, newTaskId, userName);
    }

    /**
     * 复制基础包数据
     *
     * @param newTaskId 新考试任务id
     * @param oldTaskId 旧考试任务id
     */
    private void copyBaseDataPackage(String newTaskId, String oldTaskId) {
        iExamPackageService.copyBaseDataPackage(newTaskId, oldTaskId);
    }

    /**
     * 复制考区
     *
     * @param oldTaskId 被复制的任务id
     * @param newTaskId 新的任务id
     * @param userName  用户名
     */
    private void copyExamArea(String oldTaskId, String newTaskId, String userName) {
        iExamAreaService.copyExamArea(oldTaskId, newTaskId, userName);
    }

    /**
     * 复制试卷表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyPaper(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取被复制的试卷数据
        List<Paper> paperList = iPaperService.listByTaskId(oldTaskId);
        //替换id
        paperList.forEach(paper -> {
            String oldPaperId = paper.getId();
            String newPaperId = UUIDUtils.newSortUUID();
            //替换任务编号
            paper.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换科目id
                    .setSubjectId(oldNewIdMap.get(paper.getSubjectId()))
                    //替换主键id
                    .setId(newPaperId);
            //设置旧试卷id-新试卷id的映射关系
            oldNewIdMap.put(oldPaperId, newPaperId);
        });
        //插入试卷表
        if (!iPaperService.saveBatch(paperList)) {
            //插入试卷表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入试卷表失败");
        }
    }

    /**
     * 复制批次科目试卷
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyTimeSubjectPaper(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的批次科目试卷数据
        List<TimeSubjectPaper> timeSubjectPaperList = iTimeSubjectPaperService.listByTaskId(oldTaskId);
        //替换id
        timeSubjectPaperList.forEach(timeSubjectPaper -> {
            //替换任务id
            timeSubjectPaper.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换批次id
                    .setTimeId(oldNewIdMap.get(timeSubjectPaper.getTimeId()))
                    //替换科目id
                    .setSubjectId(oldNewIdMap.get(timeSubjectPaper.getSubjectId()))
                    //替换试卷id
                    .setPaperId(oldNewIdMap.get(timeSubjectPaper.getPaperId()))
                    //替换批次科目id
                    .setTimeSubjectId(oldNewIdMap.get(timeSubjectPaper.getTimeSubjectId()))
                    //替换主键id
                    .setId(UUIDUtils.newSortUUID());
        });
        //插入批次科目试卷表
        if (!iTimeSubjectPaperService.saveBatch(timeSubjectPaperList)) {
            //插入批次科目试卷表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入批次科目试卷表失败");
        }
    }

    /**
     * 复制考生表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyCandidate(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的考生数据
        List<Candidate> candidateList = iCandidateService.listByTaskId(oldTaskId);
        //替换id
        candidateList.forEach(candidate -> {
            String oldCandidateId = candidate.getId();
            String newCandidateId = UUIDUtils.newSortUUID();
            //替换任务id
            candidate.setTaskId(oldNewIdMap.get(oldTaskId))
                    //复制任务不关联报名
                    .setBmStudentId(null)
                    //替换主键id
                    .setId(newCandidateId);
            //设置旧考生id-新考生id的映射关系
            oldNewIdMap.put(oldCandidateId, newCandidateId);
        });
        //插入考生表
        if (!iCandidateService.saveBatch(candidateList)) {
            //插入考生表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入考生表失败");
        }
    }

    /**
     * 复制考生照片表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyCandidatePhoto(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的考生照片数据
        List<CandidatePhoto> candidatePhotoList = iCandidatePhotoService.listByTaskId(oldTaskId);
        candidatePhotoList.forEach(candidatePhoto -> {
            //替换任务id
            candidatePhoto.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换考生id
                    .setCandidateId(oldNewIdMap.get(candidatePhoto.getCandidateId()))
                    //替换主键id
                    .setId(UUIDUtils.newSortUUID());
        });
        //插入考生照片表
        if (!iCandidatePhotoService.saveBatch(candidatePhotoList)) {
            //插入考生照片表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入考生照片表失败");
        }
    }

    /**
     * 复制考生科目表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyCandidateSubject(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的考生科目信息
        List<CandidateSubject> candidateSubjectList = iCandidateSubjectService.listByTaskId(oldTaskId);
        //替换id
        candidateSubjectList.forEach(candidateSubject -> {
            //替换任务id
            candidateSubject.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换考生id
                    .setCandidateId(oldNewIdMap.get(candidateSubject.getCandidateId()))
                    //替换科目id
                    .setSubjectId(oldNewIdMap.get(candidateSubject.getSubjectId()))
                    //替换主键id
                    .setId(UUIDUtils.newSortUUID());
        });
        //插入考生科目表
        if (!iCandidateSubjectService.saveBatch(candidateSubjectList)) {
            //插入考生科目表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入考生科目表失败");
        }
    }

    /**
     * 复制考生区域表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyCandidateArea(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的考生区域数据
        List<TaskCandidateArea> taskCandidateAreaList = iTaskCandidateAreaService.listByTaskId(oldTaskId);
        //替换id
        taskCandidateAreaList.forEach(taskCandidateArea -> {
            //替换任务id
            taskCandidateArea.setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换主键id
                    .setId(UUIDUtils.newSortUUID());
        });
        //插入考生区域表
        if (!iTaskCandidateAreaService.saveBatch(taskCandidateAreaList)) {
            //插入考生区域表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入考生区域表失败");
        }
    }

    /**
     * 复制编排信息表
     *
     * @param oldTaskId   旧的任务id
     * @param oldNewIdMap 旧id-新id映射关系
     * @throws KqException 异常
     */
    private void copyTimeRoomCandidate(String oldTaskId, Map<String, String> oldNewIdMap) throws KqException {
        //获取要复制的编排信息数据
        List<TimeRoomCandidate> timeRoomCandidateList = iTimeRoomCandidateService.listByTaskId(oldTaskId);
        //替换id
        timeRoomCandidateList.forEach(timeRoomCandidate -> {
            //替换考生id
            timeRoomCandidate.setCandidateId(oldNewIdMap.get(timeRoomCandidate.getCandidateId()))
                    //替换考试任务id
                    .setTaskId(oldNewIdMap.get(oldTaskId))
                    //替换场次id
                    .setTimeRoomId(oldNewIdMap.get(timeRoomCandidate.getTimeRoomId()))
                    //替换批次id
                    .setTimeId(oldNewIdMap.get(timeRoomCandidate.getTimeId()))
                    //替换考场id
                    .setRoomId(oldNewIdMap.get(timeRoomCandidate.getRoomId()))
                    //替换科目id
                    .setSubjectId(oldNewIdMap.get(timeRoomCandidate.getSubjectId()))
                    //替换考生科目id
                    .setCandidateSubjectId(oldNewIdMap.get(timeRoomCandidate.getCandidateSubjectId()))
                    //替换主键id
                    .setId(UUIDUtils.newSortUUID());
        });
        //插入编排信息表
        if (!iTimeRoomCandidateService.saveBatch(timeRoomCandidateList)) {
            //插入编排信息表失败
            throw new KqException(ResponseCode.TASK_COPY_EXCEPTION.getCode(), "插入编排信息表失败");
        }
    }


    protected void insertBreakRuleBehavior(String taskId, String officeId) throws NestedBusinessException {
        //获取机构违纪行为数据
        QueryWrapper<OfficeBreakRuleBehavior> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(OfficeBreakRuleBehavior::getLevel, CodeEnum.OFFICE_DATA.getCode())
                .eq(OfficeBreakRuleBehavior::getOfficeId, officeId)
                .eq(OfficeBreakRuleBehavior::getDelFlag, false);
        List<OfficeBreakRuleBehavior> officeBreakRuleBehaviorList = iOfficeBreakRuleBehaviorService.list(queryWrapper);
        //处理默认违纪行为数据
        List<BreakRuleBehavior> breakRuleBehaviorList = new ArrayList<>();
        for (OfficeBreakRuleBehavior behavior : officeBreakRuleBehaviorList) {
            BreakRuleBehavior breakRuleBehavior = new BreakRuleBehavior();
            BeanUtils.copyProperties(behavior, breakRuleBehavior);
            breakRuleBehavior.setTaskId(taskId).setId(UUIDUtils.newSortUUID());
            breakRuleBehaviorList.add(breakRuleBehavior);
        }
        //插入默认违纪行为数据
        if (!iBreakRuleBehaviorService.saveBatch(breakRuleBehaviorList)) {
            throw new NestedBusinessException("插入违纪行为表失败");
        }
    }

    /**
     * 插入默认违纪处理数据
     *
     * @param taskId   考试任务id
     * @param officeId 机构id
     * @throws NestedBusinessException 异常
     */
    protected void insertBreakRuleScheme(String taskId, String officeId) throws NestedBusinessException {
        //获取机构违纪处理数据
        QueryWrapper<OfficeBreakRuleScheme> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(OfficeBreakRuleScheme::getLevel, CodeEnum.OFFICE_DATA.getCode())
                .eq(OfficeBreakRuleScheme::getOfficeId, officeId)
                .eq(OfficeBreakRuleScheme::getDelFlag, false);
        List<OfficeBreakRuleScheme> officeBreakRuleSchemeList = iOfficeBreakRuleSchemeService.list(queryWrapper);
        //处理默认违纪处理数据
        List<BreakRuleScheme> breakRuleSchemeList = new ArrayList<>();
        for (OfficeBreakRuleScheme scheme : officeBreakRuleSchemeList) {
            BreakRuleScheme breakRuleScheme = new BreakRuleScheme();
            BeanUtils.copyProperties(scheme, breakRuleScheme);
            breakRuleScheme.setTaskId(taskId).setId(UUIDUtils.newSortUUID());
            breakRuleSchemeList.add(breakRuleScheme);
        }
        //插入默认违纪处理数据
        if (!iBreakRuleSchemeService.saveBatch(breakRuleSchemeList)) {
            throw new NestedBusinessException("插入违纪处理表失败");
        }
    }

    /**
     * 插入默认考试协议数据
     *
     * @param taskId   考试任务id
     * @param officeId 机构id
     * @throws NestedBusinessException 异常
     */
    protected void insertExamAgreement(String taskId, String officeId) throws NestedBusinessException {
        //获取机构考试协议模板数据
        QueryWrapper<OfficeExamAgreement> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(OfficeExamAgreement::getLevel, CodeEnum.OFFICE_DATA.getCode())
                .eq(OfficeExamAgreement::getOfficeId, officeId)
                .eq(OfficeExamAgreement::getDelFlag, false);
        List<OfficeExamAgreement> officeExamAgreementList = iOfficeExamAgreementService.list(queryWrapper);
        //处理默认考试协议数据
        List<ExamAgreement> examAgreementList = new ArrayList<>();
        for (OfficeExamAgreement agreement : officeExamAgreementList) {
            ExamAgreement examAgreement = new ExamAgreement();
            BeanUtils.copyProperties(agreement, examAgreement);
            examAgreement.setTaskId(taskId).setId(UUIDUtils.newSortUUID());
            examAgreementList.add(examAgreement);
        }
        //插入默认考试协议
        if (!iExamAgreementService.saveBatch(examAgreementList)) {
            throw new NestedBusinessException("插入考试协议表失败");
        }
    }

    /**
     * 插入考试行为分析配置
     *
     * @param taskParam 考试任务参数
     * @throws NestedBusinessException 异常
     */
    protected void insertTaskBehaviorConfig(TaskParam taskParam) throws NestedBusinessException {
        //为行为分析配置设置关联的考试任务id
        taskParam.getTaskBehaviorConfig().setTaskId(taskParam.getTask().getId());
        if (!iTaskBehaviorConfigService.save(taskParam.getTaskBehaviorConfig())) {
            throw new NestedBusinessException("插入考试行为分析配置表失败");
        }
    }

    /**
     * 插入考试系统配置
     *
     * @param taskParam 考试任务参数
     * @throws NestedBusinessException 异常
     */
    protected void insertTaskExamSysConfig(TaskParam taskParam) throws NestedBusinessException {
//        //为考试系统配置设置关联的考试任务id
//        taskParam.getTaskExamSysConfig().setTaskId(taskParam.getTask().getId());
//        if (!iTaskExamSysConfigService.save(taskParam.getTaskExamSysConfig())) {
//            throw new NestedBusinessException("插入考试系统配置表失败");
//        }
    }

    /**
     * 插入考试任务配置
     *
     * @param taskParam 考试任务参数
     * @throws NestedBusinessException 异常
     */
    protected void insertTaskConfig(TaskParam taskParam) throws NestedBusinessException {
        //为考试任务配置设置关联的考试任务id
//        taskParam.getTaskConfig().setTaskId(taskParam.getTask().getId());
//        if (!iTaskConfigService.save(taskParam.getTaskConfig())) {
//            throw new NestedBusinessException("插入考试任务配置表失败");
//        }
    }

    /**
     * 插入考试任务
     *
     * @param taskParam 考试任务参数
     * @throws NestedBusinessException 异常
     */
    protected void insertTask(TaskParam taskParam) throws NestedBusinessException {
        Task task = taskParam.getTask();
        //设置考试任务为未发布
        task.setPublish(false);
        if (!this.save(task)) {
            throw new NestedBusinessException("插入考试任务表失败");
        }
    }

    /**
     * 校验考试任务参数
     *
     * @param taskParam 考试任务参数
     * @throws GovInvalidRequestException 请求不合法异常
     */
    protected void checkTaskParam(TaskParam taskParam, String officeId) throws GovInvalidRequestException {
        // 完善校验考试任务参数
        //校验考试任务名称是否重复
        //this.checkTaskName(taskParam, officeId);
        //校验考试时间（结束时间必须大于开始时间）
        this.checkTaskTime(taskParam);
        //校验开启外网访问的情况下是否设置了外网访问密码
        this.checkTaskInternetPassword(taskParam);
        //校验提前下载授权码分钟数是否小于或等于提前下载试卷分钟数

    }

    protected void checkTaskInternetPassword(TaskParam taskParam) throws GovInvalidRequestException {
//        List<TaskConfig> taskConfigList = taskParam.getTaskConfig();
//        String internetPassword;
//        Optional<TaskConfig> o = taskConfigList.stream()
//                .filter(config -> config.getCode().equals("examClientVisitPwd"))
//                .findFirst();
//        if (o.isPresent()) {
//            internetPassword = o.get().getValue();
//        } else {
//            throw new KwGovException();
//        }
//
//        Boolean allowInternet = taskParam.getTaskExamSysConfig().getAllowInternet();
//        if (allowInternet && StringUtils.isEmpty(internetPassword)) {
//            //开启外网访问的情况下必须设置外网访问密码
//            throw new InvalidRequestException(ResponseCode.ERROR_30183, "开启外网访问的情况下必须设置外网访问密码");
//        }
    }

    protected void checkTaskTime(TaskParam taskParam) throws GovInvalidRequestException {
        LocalDateTime beginDate = taskParam.getTask().getBeginDate();
        LocalDateTime endDate = taskParam.getTask().getEndDate();
        if (endDate.isBefore(beginDate)) {
            //考试任务结束时间必须大于开始时间
            throw new GovInvalidRequestException(ResponseCode.TASK_TIME_INVALID.getCode(), "考试任务结束时间必须大于开始时间");
        }
    }

/*    protected void checkTaskName(TaskParam taskParam, String officeId) throws InvalidRequestException {
        //获取当前机构下所有考试任务
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(Task::getOfficeId, officeId)
                .eq(Task::getDelFlag, false);
        List<Task> taskList = list(queryWrapper);
        //校验考试任务名称是否重复
        if (!CollectionUtils.isEmpty(taskList)) {
            String name = taskParam.getTask().getName();
            boolean repeat = taskList.stream()
                    .anyMatch(task -> name.equals(task.getName()));
            if (repeat) {
                //当前机构下存在名称相同的考试任务
                throw new InvalidRequestException(ResponseCode.TASK_NAME_EXIST, "当前机构下存在名称相同的考试任务");
            }
        }
    }*/

    /**
     * 生成任务编号
     * 规则：年份后(2位) + 月份(2位) + 日期(2位) + 3位随机数
     *
     * @return
     */
    private String getNewTaskCode(LocalDateTime beginDate) {
        String codeRoot = DateTimeUtil.getDateShort(beginDate);
        String code = null;
        while (StringUtils.isBlank(code)) {
            //生成随机三位数
            int random = (int) ((Math.random() * 9 + 1) * 100);
            code = StringUtils.join(codeRoot, random);
            if (this.lambdaQuery().eq(Task::getCode, code).count() > 0) {
                code = null;
            }
        }
        return code;
    }

    /**
     * 生成考试任务编号
     * 生成规则：机构系统编码(5位) + 当前年月份(4位) + 当前月份自增序号
     *
     * @param taskParam 考试任务参数
     * @param officeId  机构id
     * @return 考试任务编号
     */
    protected void generateTaskCode(TaskParam taskParam, String officeId) {
        //设置机构id
        taskParam.getTask().setOfficeId(officeId);
        //获取当前月份起始日期
        LocalDate today = LocalDate.now();
        LocalDate first = LocalDate.of(today.getYear(), today.getMonth(), 1);
        LocalDate last = today.with(TemporalAdjusters.lastDayOfMonth());
        //本月的第一天
        String firstDate = first.toString() + " 00:00:00";
        //本月的最后一天
        String lastDate = last.toString() + " 23:59:59";
        //在数据库中查询当前机构、当前年月中自增序列最大的自增序号
        QueryWrapper<Task> queryWrapper = new QueryWrapper<Task>();
        queryWrapper.lambda()
                .eq(Task::getOfficeId, officeId)
                .between(Task::getCreateDate, firstDate, lastDate);
        List<Task> taskList = this.list(queryWrapper);
        //获取机构系统编码
        QueryWrapper<OfficeCode> wrapper = new QueryWrapper<OfficeCode>();
        wrapper.lambda()
                .eq(OfficeCode::getOfficeId, officeId)
                .eq(OfficeCode::getDelFlag, false);
        OfficeCode code = iOfficeCodeService.getOne(wrapper);
        //拼接机构系统编码
        StringBuffer stringBuffer = new StringBuffer(code.getCode());
        String currentMonth = DateUtils.currentMonth().replace("-", "").substring(2);
        //拼接当前年月份
        stringBuffer.append(currentMonth);
        //若前机构、当前年月中不存在考试任务，则初始化一个新的考试任务编号
        if (CollectionUtils.isEmpty(taskList)) {
            //拼接初始自增序号
            stringBuffer.append("1");
            taskParam.getTask()
                    .setCurrentMonthIndex(1)
                    .setCode(stringBuffer.toString());
            return;
        }
        //若前机构、当前年月中存在考试任务，则获取考试任务编号中最大的自增序号，从而生成新任务的自增序号
        Task lastTask = taskList.stream()
                .max(Comparator.comparing(Task::getCurrentMonthIndex)).get();
        Integer lastIndex = lastTask.getCurrentMonthIndex();
        Integer newIndex = lastIndex + 1;
        //拼接自增序号
        stringBuffer.append(newIndex.toString());
        taskParam.getTask()
                .setCurrentMonthIndex(newIndex)
                .setCode(stringBuffer.toString());
    }

    @Override
    public void isLockDataThrowException(String taskId) {
        Task task = lambdaQuery().eq(Task::getId, taskId).last("LIMIT 1").one();
        isLockDataThrowException(task);
    }

    /**
     * 检查是否已编排锁定
     *
     * @param taskId
     */
    @Override
    public void isLockDataThrowForImport(String taskId) {
        Task task = lambdaQuery().eq(Task::getId, taskId).last("LIMIT 1").one();
        Boolean dataLock = task.getDataLock();
        if (dataLock) {
            BusinessLogicException.throwException(43010106, "数据已锁定，不允许操作此功能。");
        }
    }

    @Override
    public void isLockDataThrowException(Task task) {
        Boolean dataLock = task.getDataLock();
        if (dataLock) {
            throw new KqException(ResponseCode.TASK_DATA_LOCK_TRUE.getCode(), "数据已锁定，不允许操作此功能。");
        }
    }

    @Override
    public void isNotLockDataThrowException(String taskId) {
        Task task = lambdaQuery().eq(Task::getId, taskId).last("LIMIT 1").one();
        isNotLockDataThrowException(task);
    }

    @Override
    public void isNotLockDataThrowException(Task task) {
        Boolean dataLock = task.getDataLock();
        if (!dataLock) {
            throw new KqException(ResponseCode.TASK_DATA_LOCK_FALSE.getCode(), "数据没有锁定，不允许操作此功能。");
        }
    }

    @Override
    public Integer taskDataType(String taskId) {
        Task task = lambdaQuery()
                .eq(Task::getId, taskId)
                .last("LIMIT 1")
                .one();

        return taskDataType(task);
    }

    @Override
    public Integer taskDataType(Task task) {
        if (StringUtils.isBlank(task.getTaskBmId())) {
            return CodeEnum.TASK_DATA_TYPE_NONE.getCode();
        } else if (String.valueOf(CodeEnum.TASK_DATA_IMPORT_VALUE.getCode()).equals(task.getTaskBmId()) ||
                String.valueOf(CodeEnum.TASK_DATA_IMPORT_PT_VALUE.getCode()).equals(task.getTaskBmId())) {
            return CodeEnum.TASK_DATA_TYPE_IMPORT.getCode();
        }
        return CodeEnum.TASK_DATA_TYPE_BM.getCode();
    }

    @Override
    public void checkBindBm(String taskId) {
        //如果任务绑定报名，则不允许添加考生
        Integer type = this.taskDataType(taskId);
        if (type.equals(CodeEnum.TASK_DATA_TYPE_BM.getCode())) {
            throw new KqException(ResponseCode.CANDIDATE_NONE_BM.getCode(), ResponseCode.CANDIDATE_NONE_BM.getMsg());
        }
    }

    /**
     * 检测是否绑定了报名
     *
     * @param taskId
     */
    @Override
    public void checkBindBmForImport(String taskId) {
        //如果任务绑定报名，则不允许添加考生
        Integer type = this.taskDataType(taskId);
        if (type.equals(CodeEnum.TASK_DATA_TYPE_BM.getCode())) {
            BusinessLogicException.throwException(ResponseCode.CANDIDATE_NONE_BM.getCode(), ResponseCode.CANDIDATE_NONE_BM.getMsg());
        }
    }

    @Override
    public void updateDataLock(String taskId, Boolean dataLock) {
        this.lambdaUpdate()
                .eq(Task::getId, taskId)
                .set(Task::getDataLock, dataLock)
                .update();
    }

    @Override
    public ExamManageCollectVO getExamManageData(String taskId) {
        return this.baseMapper.getExamManageData(taskId);
    }

    @Override
    public void cleanOfficeData(String officeId) {
        iOfficeAreaService.lambdaUpdate().eq(OfficeArea::getOfficeId, officeId)
                .set(OfficeArea::getDelFlag, true)
                .update();
        iOfficeBreakRuleBehaviorService.lambdaUpdate().eq(OfficeBreakRuleBehavior::getOfficeId, officeId)
                .set(OfficeBreakRuleBehavior::getDelFlag, true)
                .update();
        iOfficeBreakRuleSchemeService.lambdaUpdate().eq(OfficeBreakRuleScheme::getOfficeId, officeId)
                .set(OfficeBreakRuleScheme::getDelFlag, true)
                .update();
        iOfficeCandidateAreaService.lambdaUpdate().eq(OfficeCandidateArea::getOfficeId, officeId)
                .set(OfficeCandidateArea::getDelFlag, true)
                .update();
        iOfficeCandidateTicketTemplateService.lambdaUpdate().eq(OfficeCandidateTicketTemplate::getOfficeId, officeId)
                .set(OfficeCandidateTicketTemplate::getDelFlag, true)
                .update();
        iOfficeExamAgreementService.lambdaUpdate().eq(OfficeExamAgreement::getOfficeId, officeId)
                .set(OfficeExamAgreement::getDelFlag, true)
                .update();
        iOfficeFileTemplateService.lambdaUpdate().eq(OfficeFileTemplate::getOfficeId, officeId)
                .set(OfficeFileTemplate::getDelFlag, true)
                .update();
        iOfficePlaceService.lambdaUpdate().eq(OfficePlace::getOfficeId, officeId)
                .set(OfficePlace::getDelFlag, true)
                .update();
        iOfficeRoomService.lambdaUpdate().eq(OfficeRoom::getOfficeId, officeId)
                .set(OfficeRoom::getDelFlag, true)
                .update();
        iOfficeSoftService.lambdaUpdate().eq(OfficeSoft::getOfficeId, officeId)
                .set(OfficeSoft::getDelFlag, true)
                .update();
        iOfficeTaskConfigService.lambdaUpdate().eq(OfficeTaskConfig::getOfficeId, officeId)
                .set(OfficeTaskConfig::getDelFlag, true)
                .update();
        iOfficeTimeRoomParamService.lambdaUpdate().eq(OfficeTimeRoomParam::getOfficeId, officeId)
                .set(OfficeTimeRoomParam::getDelFlag, true)
                .update();
    }

    @Override
    public String getExamType(String taskId) {
        return this.lambdaQuery()
                .select(Task::getExamType)
                .eq(Task::getId, taskId)
                .eq(Task::getDelFlag, false)
                .last("LIMIT 1")
                .one()
                .getExamType();
    }

    @Override
    public List<TestClassifyVO> getTestClassify() {
        TestClassifyVO testClassifyVO = RemoteUtil.getOrThrows(() -> ptClient.getTreelist());
        //设置children未null，前端需要
        List<TestClassifyVO> testClassifyVOList = testClassifyVO.getChildren();
        setTreeChildrenNull(testClassifyVOList);

        return testClassifyVOList;
    }

    private void setTreeChildrenNull(List<TestClassifyVO> testClassifyVOList) {
        testClassifyVOList.stream().forEach(testClassifyVO -> {
            if (testClassifyVO.getChildren() == null) {
                return;
            }
            if (testClassifyVO.getChildren().isEmpty()) {
                testClassifyVO.setChildren(null);
            } else {
                setTreeChildrenNull(testClassifyVO.getChildren());
            }
        });
    }

    @Override
    public OnlineTaskParam getOnlineTask(String taskId) {
        OnlineTaskParam onlineTaskParam = new OnlineTaskParam();
        OnlineTaskBaseVO taskBaseVO = new OnlineTaskBaseVO();
        Task task = super.getById(taskId);
        if (task != null) {
            BeanUtils.copyProperties(task, taskBaseVO);
        }
        onlineTaskParam.setTask(taskBaseVO);
        onlineTaskParam.setTaskKsConfig(iTaskKsConfigService.lambdaQuery().select(TaskKsConfig::getConfigCode,
                        TaskKsConfig::getConfigAttribute, TaskKsConfig::getConfigGroup, TaskKsConfig::getConfigName,
                        TaskKsConfig::getConfigType, TaskKsConfig::getConfigValue, TaskKsConfig::getIconClass,
                        TaskKsConfig::getTaskId, TaskKsConfig::getId)
                .eq(TaskKsConfig::getTaskId, taskId).eq(TaskKsConfig::getDelFlag, false).list());
        return onlineTaskParam;
    }

    @Override
    public TaskInfoParam getTaskInfoParam(String taskId, String officeId) {
        if (StringUtils.isNotBlank(taskId)) {
            return assembleExistTaskInfo(taskId,officeId);
        } else {
            return assembleNotExistTaskInfo(officeId);
        }
    }

    /**
     * 装配不存在任务信息的参数
     *
     * @param officeId 机构ID
     * @return
     */
    private TaskInfoParam assembleNotExistTaskInfo(String officeId) {
        TaskInfoParam taskInfoParam = new TaskInfoParam();
        //1。任务空的
        taskInfoParam.setTaskInfoVO(new TaskInfoVO());

        //2。查询线上模式的任务配置
        taskInfoParam.setTaskOnLineConfig(iOfficeTaskKsConfigService.getTaskOnLineConfigByOfficeId(officeId));

        //3。查询线下模式的任务配置
        taskInfoParam.setTaskOffLineConfig(iOfficeTaskConfigService.getTaskOffLineConfigByOfficeId(officeId));

        // 装配任务额外信息
        TaskInfoExtParam taskInfoExtParam = assembleNotExistTaskInfoExt();
        taskInfoParam.setTaskInfoExt(taskInfoExtParam);

        return taskInfoParam;
    }

    /**
     * 装配任务额外信息
     *
     * @return
     */
    private TaskInfoExtParam assembleNotExistTaskInfoExt() {
        TaskInfoExtParam taskInfoExtParam = new TaskInfoExtParam();
        // 考试协议
        List<OfficeExamAgreement> sysDefaultAgreement = iOfficeExamAgreementService.getSysDefaultAgreement();
        for (OfficeExamAgreement officeExamAgreement : sysDefaultAgreement) {
            switch (ExamAgreementEnum.getEnumByCode(officeExamAgreement.getType())) {
                case WELCOME_WORDS:
                    taskInfoExtParam.setWelcomeWords(officeExamAgreement.getContent());
                    break;
                case EXAM_NOTICE:
                    taskInfoExtParam.setExamNotice(officeExamAgreement.getContent());
                    break;
                default:
                    break;
            }
        }
        return taskInfoExtParam;
    }

    /**
     * 装配存在任务ID参数
     *
     * @param taskId
     * @return
     */
    private TaskInfoParam assembleExistTaskInfo(String taskId,String officeId) {
        TaskInfoParam taskInfoParam = new TaskInfoParam();
        //1。查询任务
        TaskInfoVO taskInfoVO = new TaskInfoVO();
        Task task = getById(taskId);
        if(StrUtil.isNotBlank(officeId) &&  StrUtil.isNotBlank(task.getOfficeId()) && !task.getOfficeId().equals(officeId)){
            throw new KqException(ResponseCode.GENERATE_BASE_DATA_PACKAGE_TASK_NOT_EXIST.getCode(),
                    ResponseCode.GENERATE_BASE_DATA_PACKAGE_TASK_NOT_EXIST.getMsg());
        }
        BeanUtils.copyProperties(task, taskInfoVO);
        taskInfoVO.setPublishStatus(task.getPublish());
        //当前任务是否已编排
        boolean hasArranged = iTimeRoomCandidateService.hasArranged(taskId, null);
        taskInfoVO.setHasArranged(hasArranged);

        boolean hasProctorArranged = iTimeRoomExaminerService.hasArranged(taskId);
        taskInfoVO.setHasProctorArranged(hasProctorArranged);

        taskInfoParam.setTaskInfoVO(taskInfoVO);

        //线上模式
        if (TaskExamTypeEnum.ONLINE.getCode().equals(taskInfoVO.getExamType())) {
            taskInfoParam.setTaskOnLineConfig(iTaskKsConfigService.getTaskOnLineConfigByTaskId(taskId));
        } else {
            //线下模式
            taskInfoParam.setTaskOffLineConfig(iTaskConfigService.getTaskOffLineConfigByTaskId(taskId));
        }

        // 装配存在任务信息的额外信息
        TaskInfoExtParam taskInfoExtParam = assembleExistTaskInfoExt(taskId);
        // 获取平台过来的管理员ID
        List<String> userIds = iUaBusinessAccessService.getUsersByBusinessId(taskId, UserBusinessAccessTypeEnum.KW_TASK_PERMISSION.getCode());
        taskInfoExtParam.setUserIds(userIds);
        taskInfoParam.setTaskInfoExt(taskInfoExtParam);

        return taskInfoParam;
    }

    /**
     * 装配存在任务信息的额外信息
     *
     * @param taskId
     * @return
     */
    private TaskInfoExtParam assembleExistTaskInfoExt(String taskId) {
        TaskInfoExtParam taskInfoExtParam = new TaskInfoExtParam();
        // 考试协议
        List<ExamAgreementVO> examNoticeAndWelcomeWordsList = iExamAgreementService.getExamNoticeAndWelcomeWordsList(taskId);
        for (ExamAgreementVO examAgreementVO : examNoticeAndWelcomeWordsList) {
            switch (ExamAgreementEnum.getEnumByCode(examAgreementVO.getType())) {
                case WELCOME_WORDS:
                    taskInfoExtParam.setWelcomeWords(examAgreementVO.getContent());
                    break;
                case EXAM_NOTICE:
                    taskInfoExtParam.setExamNotice(examAgreementVO.getContent());
                    break;
                default:
                    break;
            }
        }
        return taskInfoExtParam;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addOrUpdateTaskConfig(TaskInfoParam taskInfoParam) throws Exception {
        User user = taskInfoParam.getUser();
        //参数校验
        this.verifyTaskParam(taskInfoParam);
        TaskInfoVO taskInfoVO = taskInfoParam.getTaskInfoVO();
        TaskInfoExtParam taskInfoExt = taskInfoParam.getTaskInfoExt();

        String taskId;
        //任务模式
        switch (TaskExamTypeEnum.getByCode(taskInfoVO.getExamType())) {
            //线上模式
            case ONLINE:
                taskId = this.addOrUpdateOnlineTask(taskInfoParam);
                break;
            //线下模式
            case OFFLINE:
                taskId = this.addOrUpdateOfflineTask(taskInfoParam);
                break;
            default:
                throw new KqException(ResponseCode.TASK_EXAM_TYPE_NOT_FOUND.getCode(),
                        ResponseCode.TASK_EXAM_TYPE_NOT_FOUND.getMsg());
        }
        if (StringUtils.isBlank(taskInfoParam.getTaskInfoVO().getId())) {
            //初始化任务参数
            this.initTaskData(taskId, iBaseLoginService.getOfficeId());
        }

        // 操作考试须知和欢迎语
        operatorExamNoticeAndWelcomeWords(taskId, taskInfoExt.getExamNotice(), taskInfoExt.getWelcomeWords(), taskInfoParam.getUser());
        // 发送平台保存
        this.saveTaskBusinessAccess(user, taskId, taskInfoExt.getUserIds(), UserBusinessAccessTypeEnum.KW_TASK_PERMISSION);
        return taskId;
    }

    /**
     * 保存任务业务权限
     *
     * @param currentLoginUser
     * @param taskId
     * @param userIds
     */
    @Override
    public void saveTaskBusinessAccess(User currentLoginUser, String taskId, List<String> userIds, UserBusinessAccessTypeEnum businessAccessTypeEnum) {
        // 删除当前登录用户对任务的访问权限
        UserBusinessAccessParam userBusinessAccessParam = new UserBusinessAccessParam();
        userBusinessAccessParam.setCurrentLoginUserId(currentLoginUser.getId());
        userBusinessAccessParam.setBusinessId(taskId);
        userBusinessAccessParam.setBusinessType(businessAccessTypeEnum.getCode());
        iUaBusinessAccessService.removeUserBusinessAccess(userBusinessAccessParam);
        if (CollUtil.isEmpty(userIds)) {
            // 如果用户ID列表为空，则返回
            return;
        }

        List<UserBusinessAccess> userBusinessAccesses = userIds.stream()
                .distinct()
                .map(userId -> {
                    UserBusinessAccess businessAccess = UserBusinessAccess.builder()
                            .userId(userId)
                            .businessType(businessAccessTypeEnum.getCode())
                            .businessId(taskId)
                            .createBy(currentLoginUser.getId())
                            .createDate(LocalDateTime.now())
                            .build();
                    return businessAccess;
                }).collect(Collectors.toList());

        UserBusinessAccessParam userBusinessAccessSave = new UserBusinessAccessParam();
        userBusinessAccessSave.setCurrentLoginUserId(currentLoginUser.getId());
        userBusinessAccessSave.setBusinessId(taskId);
        userBusinessAccessSave.setBusinessType(businessAccessTypeEnum.getCode());
        userBusinessAccessSave.setUserBusinessAccesss(userBusinessAccesses);
        iUaBusinessAccessService.saveUserBusinessAccess(userBusinessAccessSave);
    }

    /**
     * 操作考试须知和欢迎语
     *
     * @param taskId       任务ID
     * @param examNotice   考试须知
     * @param welcomeWords 欢迎语
     * @param user         用户
     */
    private void operatorExamNoticeAndWelcomeWords(String taskId, String examNotice, String welcomeWords, User user) {
        List<ExamAgreementVO> examAgreementList = iExamAgreementService.getExamNoticeAndWelcomeWordsList(taskId);
        for (ExamAgreementVO examAgreementVO : examAgreementList) {
            switch (ExamAgreementEnum.getEnumByCode(examAgreementVO.getType())) {
                case EXAM_NOTICE:
                    examAgreementVO.setContent(examNotice);
                    iExamAgreementService.saveExamNoticeAndWelcomeWords(examAgreementVO);
                    break;
                case WELCOME_WORDS:
                    examAgreementVO.setContent(welcomeWords);
                    iExamAgreementService.saveExamNoticeAndWelcomeWords(examAgreementVO);
                    break;
                default:
                    break;
            }

        }
        // 更新考生须知
        try {
            iAdmissionTicketTemplateVariableService.syncExamAgreement(taskId, user);
        } catch (Exception e) {
            log.error("[考务][操作考试须知和欢迎语][更新准考证信息]更新考试名称和时间异常，taskId：{}",taskId, e);
        }
    }

    /**
     * 校验考试任务参数
     *
     * @param taskInfoParam 考试任务参数
     */
    private void verifyTaskParam(TaskInfoParam taskInfoParam) {
        // 前端没时间开发，暂由后端校验，建议后续改为使用注解方式做参数校验，减少代码复杂度
        //考试任务名称去除空白符
        String name = StrUtil.cleanBlank(taskInfoParam.getTaskInfoVO().getName());
        taskInfoParam.getTaskInfoVO().setName(name);
        //校验考试任务名称
        String pattern = "^[\\u4E00-\\u9FA5\\w\\-]{1,40}$";
        if (!Pattern.matches(pattern, taskInfoParam.getTaskInfoVO().getName())) {
            throw new KqException(ResponseCode.TASK_NAME_NOT_MATCH.getCode(),
                    "考试任务名称只能包含中文字符、英文字符、数字、下划线和中横线，1~40个字");
        }
        LocalDateTime beginDate = taskInfoParam.getTaskInfoVO().getBeginDate();
        LocalDateTime endDate = taskInfoParam.getTaskInfoVO().getEndDate();
        if (beginDate.equals(endDate)) {
            throw new KqException(ResponseCode.TASK_START_TIME_CANNOT_BE_EQUAL_TO_THE_TASK_END_TIME.getCode(),
                    "考试任务开始时间不能等于结束时间");
        }
        //校验任务开始时间是否晚于任务结束时间
        if (beginDate.isAfter(endDate)) {
            throw new KqException(ResponseCode.TASK_SHOULD_NOT_START_LATER_THAN_THE_END_TIME.getCode(),
                    "考试任务开始时间不能晚于结束时间");
        }

        if (StrUtil.equals(taskInfoParam.getTaskInfoVO().getExamType(), TaskExamTypeEnum.ONLINE.getCode())) {
            String examNotice = taskInfoParam.getTaskInfoExt().getExamNotice();
            if (StrUtil.isBlank(examNotice)) {
                throw new KqException(43010115, "考生须知不能为空");
            }
            String welcomeWords = taskInfoParam.getTaskInfoExt().getWelcomeWords();
            if (StrUtil.isBlank(welcomeWords)) {
                throw new KqException(43010116, "欢迎语不能为空");
            }
        }

        checkTaskNameRepeat(taskInfoParam);
    }

    private void checkTaskNameRepeat(TaskInfoParam taskInfoParam) {
        if (StringUtils.isBlank(taskInfoParam.getTaskInfoVO().getId())) {
            //校验任务名称是否重复
            String officeId = iBaseLoginService.getOfficeId();
            Integer count = this.lambdaQuery()
                    .eq(Task::getName, taskInfoParam.getTaskInfoVO().getName())
                    .eq(Task::getOfficeId, officeId)
                    .eq(BaseEntity::getDelFlag, false)
                    .last("LIMIT 1")
                    .count();
            if (count > 0) {
                throw new KqException(ResponseCode.TASK_NAME_ARE_REPEATED.getCode(),
                        "考试任务名称" + "”" + taskInfoParam.getTaskInfoVO().getName() + "”" + "重复");
            }
        }
    }

    /**
     * 新增和编辑考试配置，-线上模式
     *
     * @param taskInfoParam
     * @return
     */
    //@GlobalTransactional(rollbackFor = Exception.class)
    private String addOrUpdateOnlineTask(TaskInfoParam taskInfoParam) {

        Task task = new Task();
        BeanUtils.copyProperties(taskInfoParam.getTaskInfoVO(), task);

        //是否编辑
        boolean flag = StringUtils.isBlank(task.getId()) ? false : true;

        //1.处理任务的基本数据
        if (!flag) {
            //新增
            task.setId(UUIDUtils.newSortUUID());
            iTaskKsConfigService.validExamConfig(task, taskInfoParam.getTaskOnLineConfig(), 1);
            task.setPublish(false)
                    .setOfficeId(iBaseLoginService.getOfficeId())
                    .setDeptId(iBaseLoginService.getDeptId())
                    .setType(CodeEnum.TASK_TYPE_FORMAL.getCode())
                    .setDataLock(false)
                    .setCode(this.getNewTaskCode(task.getBeginDate()))
                    .setDelFlag(false);

            this.save(task);
        } else {
            // 锁定编排不允许修改，暂时放宽，仅受考试系统约束
            //this.isLockDataThrowException(task.getId());

            //编辑
            iTaskKsConfigService.validExamConfig(task, taskInfoParam.getTaskOnLineConfig(), 2);
            this.updateById(task);
        }

        //任务参数
        List<TaskKsConfig> taskKsConfigs = new ArrayList<>();
        taskInfoParam.getTaskOnLineConfig().stream().forEach(taskOffLineConfig -> {
            TaskKsConfig taskKsConfig = new TaskKsConfig();
            BeanUtils.copyProperties(taskOffLineConfig, taskKsConfig);

            String code = taskOffLineConfig.getConfigCode() != null ? taskOffLineConfig.getConfigCode().trim() : taskOffLineConfig.getConfigCode();
            String value = taskOffLineConfig.getConfigValue() != null ? taskOffLineConfig.getConfigValue().trim() : taskOffLineConfig.getConfigValue();

            taskKsConfig.setConfigCode(code)
                    .setConfigValue(value)
                    .setTaskId(task.getId());

            //如果是新增，则需要重新定义任务参数Id
            if (!flag) {
                taskKsConfig.setId(UUIDUtils.newSortUUID());
            }

            taskKsConfigs.add(taskKsConfig);
        });

        //2。新增和编辑入库操作
        iTaskKsConfigService.saveOrUpdateBatch(taskKsConfigs);

        //3.如果是编辑功能，则推送数据
        if (flag && iTimeService.exists(task.getId())) {
            //存在批次数据，才允许推送
            PushExamConfigVO pushExamConfigVO = new PushExamConfigVO();
            pushExamConfigVO.setTaskId(task.getId())
                    .setTaskName(task.getName())
                    .setPushExamConfigParams(iTaskKsConfigService.getExamConfigNoFalse(task.getId()));

            log.info("推送线上任务参数:{}", JSON.toJSONString(pushExamConfigVO));
            ApiResponse<Object> res = ksClient.pushExamConfig(pushExamConfigVO);
            log.info("推送线上任务结果:{}", JSON.toJSONString(res));
            if (res == null) {
                throw new KqException(ResponseCode.EXAM_CONFIG_PUSH_KS_RESULT_NONE.getCode(), "推送考试配置数据失败，进入熔断。");
            }
            if (!ResponseCode.SUCCESS.getCode().equals(res.getCode())) {
                throw new KqException(res.getCode(), "推送接口报错。");
            }
        }
        return task.getId();
    }

    /**
     * 新增和编辑考试配置，-线下模式
     *
     * @param taskInfoParam
     * @return
     */
    private String addOrUpdateOfflineTask(TaskInfoParam taskInfoParam) {

        Task task = new Task();
        BeanUtils.copyProperties(taskInfoParam.getTaskInfoVO(), task);

        //是否编辑
        boolean flag = StringUtils.isBlank(task.getId()) ? false : true;

        //1.处理任务的基本数据
        if (!flag) {
            //新增
            task.setPublish(false)
                    .setOfficeId(iBaseLoginService.getOfficeId())
                    .setDeptId(iBaseLoginService.getDeptId())
                    .setType(CodeEnum.TASK_TYPE_FORMAL.getCode())
                    .setDataLock(false)
                    .setCode(this.getNewTaskCode(task.getBeginDate()))
                    .setDelFlag(false)
                    .setId(UUIDUtils.newSortUUID());
            this.save(task);
        } else {
            // 锁定编排不允许修改
            this.isLockDataThrowException(task.getId());
            //编辑
            this.updateById(task);
        }

        //2。任务参数
        List<TaskConfig> taskConfigs = new ArrayList<>();
        taskInfoParam.getTaskOffLineConfig().stream().forEach(taskOffLineConfig -> {
            TaskConfig taskConfig = new TaskConfig();
            BeanUtils.copyProperties(taskOffLineConfig, taskConfig);
            taskConfig.setCode(taskOffLineConfig.getConfigCode())
                    .setValue(taskOffLineConfig.getConfigValue())
                    .setTaskId(task.getId());

            if (!flag) {
                taskConfig.setId(null);
            }

            taskConfigs.add(taskConfig);
        });

        iTaskConfigService.saveOrUpdateBatch(taskConfigs);

        return task.getId();
    }

    @Override
    public void updateTaskBmId(String taskId, String taskBmId) {
        this.lambdaUpdate()
                .eq(Task::getId, taskId)
                .set(Task::getTaskBmId, taskBmId)
                .update();
    }

    @Override
    public String getTaskSignUpId(String taskId) {
        Task task = this.lambdaQuery()
                .select(Task::getTaskBmId)
                .eq(Task::getId, taskId)
                .eq(Task::getDelFlag, false)
                .last("LIMIT 1")
                .one();
        String TaskSignUpId = Optional.ofNullable(task)
                .map(Task::getTaskBmId)
                .orElseThrow(() -> new KqException(ResponseCode.TASK_NOT_EXIST.getCode(), "考试任务不存在"));
        if (StringUtils.isBlank(TaskSignUpId)
                || StringCodeEnum.TASK_SIGN_UP_ID_IMPORT.getCode().equals(TaskSignUpId)) {
            throw new KqException(ResponseCode.TASK_HAS_NOT_YET_BEEN_BOUND_FOR_REGISTRATION.getCode(),
                    ResponseCode.TASK_HAS_NOT_YET_BEEN_BOUND_FOR_REGISTRATION.getMsg());
        }
        return TaskSignUpId;
    }

    @Override
    public String getIdBySignUpId(String signUpId) {
        List<Task> taskList = this.lambdaQuery()
                .select(Task::getId)
                .eq(Task::getTaskBmId, signUpId)
                .eq(Task::getDelFlag, false)
                .list();
        if (CollectionUtils.isEmpty(taskList)) {
            //报名id对应的考试任务不存在
            throw new KqException(ResponseCode.TASK_NOT_EXIST.getCode(),
                    "报名id对应的考试任务不存在");
        }
        if (taskList.size() > 1) {
            //存在多个绑定相同报名的考试任务
            throw new KqException(ResponseCode.DUPLICATE_TASK_BEEN_BOUND_REGISTRATION_FOUND.getCode(),
                    ResponseCode.DUPLICATE_TASK_BEEN_BOUND_REGISTRATION_FOUND.getMsg());
        }
        return taskList.stream()
                .findFirst()
                .map(Task::getId)
                .orElseThrow(() -> new KqException(ResponseCode.TASK_NOT_EXIST.getCode(),
                        "通过报名id取值考试任务id失败"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindPlatform(String taskIds) {
        this.updateBindPlatform(taskIds, Boolean.TRUE);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbindPlatform(String taskIds) {
        this.updateBindPlatform(taskIds, Boolean.FALSE);
    }

    private void updateBindPlatform(String taskIds, Boolean bind) {
        List<String> taskIdList = Arrays.asList(taskIds.split(","));
        this.lambdaUpdate()
                .in(Task::getId, taskIdList)
                .set(Task::getBindPlatform, bind)
                .update();
    }

    @Override
    public List<TaskStatisticsVO> listTaskStatistics(String taskIds) {
        //转换考试任务id
        List<String> taskIdList = Arrays.asList(taskIds.split(","));
        //获取考试任务主表信息
        List<Task> taskList = this.lambdaQuery()
                .select(Task::getId, Task::getName)
                .in(Task::getId, taskIdList)
                .eq(Task::getDelFlag, Boolean.FALSE)
                .orderByDesc(Task::getCreateDate)
                .list();
        //统计考试任务信息
        return taskList.stream().map(task -> {
            //获取考试任务id
            String taskId = task.getId();
            //获取编排考生数
            Integer arrangedCount = iTimeRoomCandidateService.countArrangedCandidate(taskId);
            //获取科目数
            Integer subjectCount = iSubjectService.countByTaskId(taskId);
            //获取批次数
            Integer timeCount = iTimeService.countByTaskId(taskId);
            //设置考试任务统计信息数据
            TaskStatisticsVO taskStatisticsVO = new TaskStatisticsVO();
            taskStatisticsVO.setId(task.getId())
                    .setName(task.getName())
                    .setArrangedCount(arrangedCount)
                    .setSubjectCount(subjectCount)
                    .setTimeCount(timeCount);
            return taskStatisticsVO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<Task> getTaskListLimit(Integer limit) {
        return taskMapper.getTaskListLimit(limit);

    }

    /**
     * 是否报名导入的任务
     *
     * @param taskId
     * @return
     */
    @Override
    public boolean isBmImport(String taskId) {
        Task task = getById(taskId);
        if (Objects.nonNull(task)) {
            return StrUtil.isNotBlank(task.getTaskBmId())
                    && !"0".equals(task.getTaskBmId());
        } else {
            return false;
        }
    }

    /**
     * 是否发布
     *
     * @param taskId
     * @return
     */
    @Override
    public boolean isPublish(String taskId) {
        Task task = getById(taskId);
        return Objects.nonNull(task) && Objects.equals(task.getPublish(), true);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deleteTask(String taskId) {
        Task task = getById(taskId);
        if (task == null) {
            BusinessLogicException.throwException(43010702, "无效的考务任务");
        }
        if (task.getPublish()) {
            BusinessLogicException.throwException(43010703, "无法操作已发布的考务任务");
        }

        this.lambdaUpdate()
                .eq(BaseEntity::getId, taskId)
                .set(Task::getDelFlag, Boolean.TRUE)
                .set(Task::getUpdateBy, iBaseLoginService.getUserId())
                .set(Task::getUpdateDate, LocalDateTime.now())
                .update();

        // 这里调用请求考试删除接口
        RemoteUtil.getOrThrows(() -> ksClient.deleteTask(taskId));
        return Boolean.TRUE;
    }

    @Override
    public void isPublishAndDataLockThrowException(String taskId) {
        Task task = getById(taskId);
        if (task.getPublish()) {
            throw new KqException(ResponseCode.TASK_PUBLISH_TRUE.getCode(), "任务已发布，不允许操作此功能。");
        }
        Boolean dataLock = task.getDataLock();
        if (dataLock) {
            throw new KqException(ResponseCode.TASK_DATA_LOCK_TRUE.getCode(), "数据已锁定，不允许操作此功能。");
        }
    }
}
